﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows.Forms;
using DevExpress.XtraEditors;
using DevExpress.XtraEditors.DXErrorProvider;
using DevExpress.XtraGrid.Views.Grid;
using Loki.Commands;
using Loki.Engine;
using Loki.Utils;

namespace Loki.UI.Win
{
    public class ViewControlBase : XtraUserControl, IView
    {


        #region Properties
        protected bool NameChangeOnVMDirty
        {
            get;
            set;
        }
        #endregion

        #region Events
        private readonly object ClosedEventLock = new object();
        private EventHandler ClosedEvent;

        /// <summary>
        /// Event raised after the <see cref="Text" /> property value has changed.
        /// </summary>
        public event EventHandler Closed
        {
            add
            {
                lock (ClosedEventLock)
                {
                    ClosedEvent += value;
                }
            }
            remove
            {
                lock (ClosedEventLock)
                {
                    ClosedEvent -= value;
                }
            }
        }

        /// <summary>
        /// Raises the <see cref="Closed" /> event.
        /// </summary>
        /// <param name="e"><see cref="EventArgs" /> object that provides the arguments for the event.</param>
        protected virtual void OnClosed(EventArgs e)
        {
            EventHandler handler = null;

            lock (ClosedEventLock)
            {
                handler = ClosedEvent;

                if (handler == null)
                    return;
            }

            handler(this, e);
        }
        #endregion

        protected DevExpress.XtraLayout.LayoutControl LAY_Root;
        protected DevExpress.XtraLayout.LayoutControlGroup LAG_Root;
        private WaitControl CTR_Wait;

        public ViewControlBase()
        {
            Commands = new CommandManager();
            DataBinder = new Databinder();
            NameChangeOnVMDirty = true;
            InitializeComponent();
            BindingChanged += InitializeBindings;
        }

        private void InitializeComponent()
        {
            this.LAY_Root = new DevExpress.XtraLayout.LayoutControl();
            this.LAG_Root = new DevExpress.XtraLayout.LayoutControlGroup();
            ((System.ComponentModel.ISupportInitialize)(this.LAY_Root)).BeginInit();
            ((System.ComponentModel.ISupportInitialize)(this.LAG_Root)).BeginInit();
            this.SuspendLayout();
            // 
            // LAY_Root
            // 
            this.LAY_Root.Dock = System.Windows.Forms.DockStyle.Fill;
            this.LAY_Root.Location = new System.Drawing.Point(0, 0);
            this.LAY_Root.Name = "LAY_Root";
            this.LAY_Root.Root = this.LAG_Root;
            this.LAY_Root.Size = new System.Drawing.Size(150, 150);
            this.LAY_Root.TabIndex = 0;
            this.LAY_Root.Text = "LAY_Root";
            // 
            // LAG_Root
            // 
            this.LAG_Root.CustomizationFormText = "Root";
            this.LAG_Root.EnableIndentsWithoutBorders = DevExpress.Utils.DefaultBoolean.True;
            this.LAG_Root.GroupBordersVisible = false;
            this.LAG_Root.Location = new System.Drawing.Point(0, 0);
            this.LAG_Root.Name = "LAG_Root";
            this.LAG_Root.Size = new System.Drawing.Size(150, 150);
            this.LAG_Root.Spacing = new DevExpress.XtraLayout.Utils.Padding(0, 0, 0, 0);
            this.LAG_Root.Padding = new DevExpress.XtraLayout.Utils.Padding(0);
            this.LAG_Root.Text = "Root";
            this.LAG_Root.TextVisible = false;
            // 
            // ViewControlBase
            // 
            this.Controls.Add(this.LAY_Root);
            this.Controls.SetChildIndex(this.LAY_Root, 0);
            this.Name = "ViewControlBase";
            ((System.ComponentModel.ISupportInitialize)(this.LAY_Root)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this.LAG_Root)).EndInit();
            this.ResumeLayout(false);

        }

        #region Binding
        protected internal Databinder DataBinder
        {
            get;
            private set;
        }
        #endregion

        #region BindingChanged
        private readonly object BindingChangedEventLock = new object();
        private EventHandler BindingChangedEvent;

        public event EventHandler BindingChanged
        {
            add
            {
                lock (BindingChangedEventLock)
                {
                    BindingChangedEvent += value;
                }
            }
            remove
            {
                lock (BindingChangedEventLock)
                {
                    BindingChangedEvent -= value;
                }
            }
        }

        protected virtual void OnBindingChanged(EventArgs e)
        {
            EventHandler handler = null;

            lock (BindingChangedEventLock)
            {
                handler = BindingChangedEvent;

                if (handler == null)
                    return;
            }

            handler(this, e);
        }

        #endregion

        #region BindingChanging
        private readonly object BindingChangingEventLock = new object();
        private EventHandler BindingChangingEvent;

        public event EventHandler BindingChanging
        {
            add
            {
                lock (BindingChangingEventLock)
                {
                    BindingChangingEvent += value;
                }
            }
            remove
            {
                lock (BindingChangingEventLock)
                {
                    BindingChangingEvent -= value;
                }
            }
        }

        protected virtual void OnBindingChanging(EventArgs e)
        {
            EventHandler handler = null;

            lock (BindingChangingEventLock)
            {
                handler = BindingChangingEvent;

                if (handler == null)
                    return;
            }

            handler(this, e);
        }

        #endregion

        private IViewModel _ViewModel;

        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public IViewModel ViewModel
        {
            get
            {
                return _ViewModel;
            }
            set
            {
                if (_ViewModel != null)
                {
                    OnBindingChanging(EventArgs.Empty);
                }

                _ViewModel = value;

                if (_ViewModel != null && FindForm() != null)
                {
                    _ViewModel.Initialize();
                    OnBindingChanged(EventArgs.Empty);
                }
            }
        }

        private void InitializeBindings(object sender, EventArgs e)
        {
            this.WatchChange(ViewModel, x => x.ModalViewModel, v => v.ViewModel_ModalViewModelChanged);
            this.WatchChange(ViewModel, v => v.Status, c => c.ViewModel_StatusChanged);

            HandleValidation(ViewModel.Validate, Validators.ToArray());
        }

        #region Log
        /// <summary>
        /// Gets the logger.
        /// </summary>
        /// <value>The logger.</value>
        protected ILog Log
        {
            get
            {
                if (_Log == null)
                    _Log = LokiServices.Logger.GetLogger("Loki.UI.Win.ViewControlBase");
                return _Log;
            }
        }
        private static ILog _Log = null;
        #endregion

        private void ViewModel_ModalViewModelChanged(object sender, PropertyChangedEventArgs e)
        {
            if (ViewModel.ModalViewModel != null)
            {
                ConfirmViewModel L_Dialog = ViewModel.ModalViewModel as ConfirmViewModel;
                if (L_Dialog != null)
                {
                    L_Dialog.DialogResult = MessageBoxes.Confirm(L_Dialog.Message);
                }
                else
                {
                    IWinform L_View = ContextHelper.GetView<IWinform>(ViewModel.ModalViewModel);
                    L_View.ShowDialog();
                }

            }
        }

        void ViewModel_StatusChanged(object sender, PropertyChangedEventArgs e)
        {
            if (ViewModel != null)
            {
                Action L_Actor = delegate()
                {
                    if (CTR_Wait == null)
                    {
                        CTR_Wait = new WaitControl();
                        // 
                        // CTR_Wait
                        // 
                        this.CTR_Wait.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
                        this.CTR_Wait.Location = new System.Drawing.Point(109, 12);
                        this.CTR_Wait.Name = "CTR_Wait";
                        this.CTR_Wait.Padding = new System.Windows.Forms.Padding(10);
                        this.CTR_Wait.Size = new System.Drawing.Size(430, 80);
                        this.CTR_Wait.TabIndex = 4;
                        this.CTR_Wait.Visible = false;
                        this.Controls.Add(CTR_Wait);
                        this.Controls.SetChildIndex(CTR_Wait, 0);

                    }
                    Control L_Form = this.FindForm();
                    if (!string.IsNullOrWhiteSpace(ViewModel.Status))
                    {
                        CTR_Wait.Parent = this;
                        CTR_Wait.Height = 80;
                        CTR_Wait.LBL_Text.Text = ViewModel.Status;

                        LAY_Root.Layout += ViewControlBase_Layout;
                        LAY_Root.Resize += ViewControlBase_Layout;
                        CTR_Wait.Left = LAY_Root.Left + (LAY_Root.Width - CTR_Wait.Width) / 2;
                        CTR_Wait.Top = LAY_Root.Top + (LAY_Root.Height - CTR_Wait.Height) / 2;
                        Log.Debug("Left:" + CTR_Wait.Left + " Top:" + CTR_Wait.Top);
                        LAY_Root.Enabled = false;
                        CTR_Wait.Show();
                    }
                    else
                    {
                        LAY_Root.Enabled = true;
                        LAY_Root.Layout -= ViewControlBase_Layout;
                        LAY_Root.Resize -= ViewControlBase_Layout;
                        CTR_Wait.Hide();
                    }
                };
                if (!IsDisposed && IsHandleCreated)
                {
                    if (InvokeRequired)
                        BeginInvoke(L_Actor);
                    else
                        L_Actor();
                }
            }
        }

        void ViewControlBase_Layout(object sender, EventArgs e)
        {
            CTR_Wait.Left = LAY_Root.Left + (LAY_Root.Width - CTR_Wait.Width) / 2;
            CTR_Wait.Top = LAY_Root.Top + (LAY_Root.Height - CTR_Wait.Height) / 2;
            Log.Debug("Left:" + CTR_Wait.Left + " Top:" + CTR_Wait.Top);
        }

        #region Command handlers
        protected CommandManager Commands { get; private set; }
        #endregion

        #region Dispose
        protected virtual void ClearViewModelEvents() { }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                // dispose commands
                Commands.Dispose();

                // dispose binder
                DataBinder.Dispose();
            }
            base.Dispose(disposing);
        }

        #endregion

        #region Validation
        protected internal virtual IEnumerable<DXValidationProvider> Validators
        {
            get { return new List<DXValidationProvider>(); }
        }

        protected void IfValid(ICommand P_Command, params DXValidationProvider[] P_Validators)
        {
            IfValid(() => P_Command.Execute(null), P_Validators);
        }

        protected void IfValid(Action P_IfValidMethod, params DXValidationProvider[] P_Validators)
        {
            IEnumerable<ValidationMessage> L_Errors = ValidateData(false, P_Validators);
            if (!L_Errors.Any())
                P_IfValidMethod();
            else
                ErrorSummary.DisplayErrorSummary(L_Errors.ToArray());
        }

        protected virtual IEnumerable<ValidationMessage> ValidateData(bool L_UseWarnings = false, params DXValidationProvider[] P_Validators)
        {
            List<ValidationMessage> L_Return = new List<ValidationMessage>();
            IEnumerable<DXValidationProvider> L_ValidatorList = P_Validators;
            if (L_ValidatorList == null)
                L_ValidatorList = Validators;
            foreach (DXValidationProvider L_Validator in L_ValidatorList)
            {
                if (!L_Validator.Validate())
                {
                    foreach (Control L_Control in L_Validator.GetInvalidControls())
                    {
                        ValidationRuleBase L_Rule = L_Validator.GetValidationRule(L_Control);
                        if (L_UseWarnings)
                            L_Rule.ErrorType = ErrorType.Warning;
                        else
                            L_Rule.ErrorType = ErrorType.Default;
                        L_Validator.Validate();

                        L_Return.Add(new ValidationMessage() { Message = L_Rule.ErrorText, Type = L_Rule.ErrorType });
                    }
                }
            }
            return L_Return;
        }

        protected void ExecuteOnClick(Control P_Button, ICommand P_Command, params DXValidationProvider[] P_Validators)
        {
            P_Button.Click += delegate(object P_Sender, EventArgs e)
            {
                IfValid(P_Command, P_Validators);
            };

            foreach (var item in P_Validators)
            {
                DataBinder.AddValidator(item);
            }
        }

        private void HandleValidation(ICommand P_Command, params DXValidationProvider[] P_Validators)
        {
            Commands.Handle(P_Command, (s, e) => { e.CanExecute |= ViewModel.ValidationEnabled; }, (s, e) =>
            {
                ValidationMode L_Mode = e.Parameter == null ? ValidationMode.None : (ValidationMode)e.Parameter;
                UIContext.ProtectUIDispatcher((state) => Validate(L_Mode, P_Validators));
            });
            foreach (var item in P_Validators)
            {
                DataBinder.AddValidator(item);
            }

        }
        protected virtual void Validate(ValidationMode P_Mode, params DXValidationProvider[] P_Validators)
        {
            bool L_UseWarnings = P_Mode == ValidationMode.SilentWarning || P_Mode == ValidationMode.Warning;
            bool L_Silent = P_Mode == ValidationMode.SilentError || P_Mode == ValidationMode.SilentWarning;
            if (P_Mode != ValidationMode.None)
            {
                IEnumerable<ValidationMessage> L_Messages = ValidateData(L_UseWarnings, P_Validators);
                if (!L_Silent && L_Messages.Any())
                {
                    InputErrorMessageBox.DisplayErrorSummary(L_Messages.ToArray());
                }

                if (ViewModel != null && ViewModel.IsValid)
                {
                    ViewModel.IsValid &= !L_Messages.Any();
                }
            }
            else
            {
                foreach (DXValidationProvider L_Validator in P_Validators)
                {
                    foreach (Control L_Control in L_Validator.GetInvalidControls())
                    {
                        L_Validator.RemoveControlError(L_Control);
                    }
                }
            }
        }
        #endregion

        protected void ViewModel_CheckCancelModifications(object sender, CancelEventArgs e)
        {
            if (ViewModel.IsDirty)
            {
                e.Cancel |= !MessageBoxes.Confirm(Loki.Messages.VMMessages.CONFIRM_EDIT_LOSS);
            }
        }

        protected void ExportGridsXls(params GridView[] P_Grids)
        {
            foreach (var L_Grid in P_Grids)
            {
                L_Grid.ExportToXls();
            }
        }

        protected void ExportGridsXls(string P_Title, params GridView[] P_Grids)
        {
            foreach (var L_Grid in P_Grids)
            {
                string L_OldCaption = string.Empty;
                bool L_OldCaptionVisible = false;
                if (L_Grid.OptionsView.ShowViewCaption)
                {
                    L_OldCaptionVisible = true;
                    L_OldCaption = L_Grid.ViewCaption;
                }
                if (!string.IsNullOrWhiteSpace(P_Title))
                    L_Grid.ViewCaption = string.Format("{0} - {1}", P_Title, L_Grid.ViewCaption);
                L_Grid.OptionsView.ShowViewCaption = true;

                L_Grid.ExportToXls();

                L_Grid.OptionsView.ShowViewCaption = L_OldCaptionVisible;
                L_Grid.ViewCaption = L_OldCaption;
            }
        }
    }
}
